using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using Extention.ENode.Rpc.ApiGateway.OAuth;
using Extention.ENode.Rpc.ApiGateway.OAuth.Impl;
using Extention.ENode.Rpc.Attributes;

using Extention.ENode.Rpc.Proxy;
using Extention.ENode.Rpc.Proxy.Impl;
using Extention.ENode.Rpc.Routing;
using Extention.ENode.Rpc.Routing.Impl;
using Extention.ENode.Rpc.Runtime.Client;
using Extention.ENode.Rpc.Runtime.Client.Address.Resolvers;
using Extention.ENode.Rpc.Runtime.Client.Address.Resolvers.Implementation;
using Extention.ENode.Rpc.Runtime.Client.Address.Resolvers.Implementation.Selectors;
using Extention.ENode.Rpc.Runtime.Client.Address.Resolvers.Implementation.Selectors.Implementation;
using Extention.ENode.Rpc.Runtime.Client.HealthChecks;
using Extention.ENode.Rpc.Runtime.Client.HealthChecks.Implementation;
using Extention.ENode.Rpc.Runtime.Client.Implementation;
using Extention.ENode.Rpc.Runtime.Communally.Convertibles;
using Extention.ENode.Rpc.Runtime.Communally.Convertibles.Impl;
using Extention.ENode.Rpc.Runtime.Communally.IdGenerator;
using Extention.ENode.Rpc.Runtime.Communally.IdGenerator.Impl;
using Extention.ENode.Rpc.Runtime.Communally.Serialization;
using Extention.ENode.Rpc.Runtime.Communally.Serialization.Implementation;
using Extention.ENode.Rpc.Runtime.Server;
using Extention.ENode.Rpc.Runtime.Server.Impl;
using Extention.ENode.Rpc.Transport;
using Extention.ENode.Rpc.Transport.Codec;
using Extention.ENode.Rpc.Transport.Codec.Implementation;
using Extention.ENode.Rpc.Transport.Impl;
using Extention.ENode.Rpc.Runtime.Communally.Entitys.Address;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ECommon.Components;
using Extention.ENode.Cache;

namespace Extention.ENode.Rpc.DependencyResolver.Builder
{
    public static class RpcServiceExtensions
    {
        /// <summary>
        /// 添加Json序列化支持
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <returns>Rpc服务构建者</returns>
        private static IRpcBuilder AddJsonSerialization(this IRpcBuilder builder)
        {
            var services = builder.Services;

            services.AddSingleton<ISerializer<string>, JsonSerializer>();
            services.AddSingleton<ISerializer<byte[]>, StringByteArraySerializer>();
            services.AddSingleton<ISerializer<object>, StringObjectSerializer>();

            return builder;
        }

        #region RouteManager

        /// <summary>
        /// 设置服务路由管理者
        /// </summary>
        /// <typeparam name="T">服务路由管理者实现</typeparam>
        /// <param name="builder">Rpc服务构建者</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseRouteManager<T>(this IRpcBuilder builder) where T : class, IServiceRouteManager
        {
            builder.Services.AddSingleton<IServiceRouteManager, T>();
            return builder;
        }

        /// <summary>
        /// 设置服务路由管理者
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <param name="factory">服务路由管理者实例工厂</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseRouteManager(this IRpcBuilder builder, Func<IServiceProvider, IServiceRouteManager> factory)
        {
            builder.Services.AddSingleton(factory);
            return builder;
        }

        /// <summary>
        /// 设置服务路由管理者
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <param name="instance">服务路由管理者实例</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseRouteManager(this IRpcBuilder builder, IServiceRouteManager instance)
        {
            builder.Services.AddSingleton(instance);
            return builder;
        }

        #endregion RouteManager


        /// <summary>
        /// 设置共享文件路由管理者
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <param name="filePath">文件路径</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseRedisRouteManager(this IRpcBuilder builder)
        {
            return builder.UseRouteManager(provider =>
                new RedisServiceRouterManager(
                    provider.GetRequiredService<ISerializer<byte[]>>(),
                    provider.GetRequiredService<ISerializer<string>>(),
                    provider.GetRequiredService<IServiceRouteFactory>(),
                    provider.GetRequiredService<ILogger<RedisServiceRouterManager>>(),
                    provider.GetRequiredService<ICacheProvider>()
                ));
        }


        #region AddressSelector

        /// <summary>
        /// 设置服务地址选择器
        /// </summary>
        /// <typeparam name="T">地址选择器实现类型</typeparam>
        /// <param name="builder">Rpc服务构建者</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseAddressSelector<T>(this IRpcBuilder builder) where T : class, IAddressSelector
        {
            builder.Services.AddSingleton<IAddressSelector, T>();
            return builder;
        }

        /// <summary>
        /// 设置服务地址选择器
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <param name="factory">服务地址选择器实例工厂</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseAddressSelector(this IRpcBuilder builder,
            Func<IServiceProvider, IAddressSelector> factory)
        {
            builder.Services.AddSingleton(factory);

            return builder;
        }

        /// <summary>
        /// 设置服务地址选择器
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <param name="instance">地址选择器实例</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseAddressSelector(this IRpcBuilder builder, IAddressSelector instance)
        {
            builder.Services.AddSingleton(instance);

            return builder;
        }

        #endregion AddressSelector

        /// <summary>
        /// 使用轮询的地址选择器
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UsePollingAddressSelector(this IRpcBuilder builder)
        {
            builder.Services.AddSingleton<IAddressSelector, PollingAddressSelector>();
            return builder;
        }

        /// <summary>
        /// 使用随机的地址选择器
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseRandomAddressSelector(this IRpcBuilder builder)
        {
            builder.Services.AddSingleton<IAddressSelector, RandomAddressSelector>();
            return builder;
        }

        #region Codec Factory

        /// <summary>
        /// 使用编解码器
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <param name="codecFactory"></param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseCodec(this IRpcBuilder builder, ITransportMessageCodecFactory codecFactory)
        {
            builder.Services.AddSingleton(codecFactory);
            return builder;
        }

        /// <summary>
        /// 使用编解码器
        /// </summary>
        /// <typeparam name="T">编解码器工厂实现类型</typeparam>
        /// <param name="builder">Rpc服务构建者</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseCodec<T>(this IRpcBuilder builder) where T : class, ITransportMessageCodecFactory
        {
            builder.Services.AddSingleton<ITransportMessageCodecFactory, T>();
            return builder;
        }

        /// <summary>
        /// 使用编解码器
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <param name="codecFactory">编解码器工厂创建委托</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseCodec(this IRpcBuilder builder,
            Func<IServiceProvider, ITransportMessageCodecFactory> codecFactory)
        {
            builder.Services.AddSingleton(codecFactory);

            return builder;
        }

        #endregion Codec Factory

        /// <summary>
        /// 使用Json编解码器
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder UseJsonCodec(this IRpcBuilder builder)
        {
            return builder.UseCodec<JsonTransportMessageCodecFactory>();
        }

        /// <summary>
        /// 添加客户端运行时服务
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder AddClientRuntime(this IRpcBuilder builder)
        {
            var services = builder.Services;

            services.AddSingleton<IHealthCheckService, DefaultHealthCheckService>();
            services.AddSingleton<IAddressResolver, DefaultAddressResolver>();
            services.AddSingleton<IRemoteInvokeService, RemoteInvokeService>();

            return builder.UsePollingAddressSelector();
        }

        /// <summary>
        /// 添加服务运行时服务
        /// </summary>
        /// <param name="builder">Rpc服务构建者</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder AddService(this IRpcBuilder builder)
        {
            var services = builder.Services;
            services.AddSingleton<IServiceEntryFactory, ServiceEntryFactory>();
            services.AddSingleton<IServiceEntryProvider>(provider =>
                new AttributeServiceEntryProvider(
                    AppDomain.CurrentDomain.GetAssemblies().AsParallel()
                    .Where(i => !i.FullName.Contains("MySql.Data"))
                    .Where(i => i.IsDynamic == false)
                    .SelectMany(i => i.ExportedTypes)
                        .ToArray(),
                    provider.GetRequiredService<IServiceEntryFactory>(),
                    provider.GetRequiredService<ILogger<AttributeServiceEntryProvider>>()));
            services.AddSingleton<IServiceEntryManager, DefaultServiceEntryManager>();
            services.AddSingleton<IServiceEntryLocate, DefaultServiceEntryLocate>();
            services.AddSingleton<IServiceExecutor, DefaultServiceExecutor>();

            return builder;
        }

        /// <summary>
        /// 添加RPC核心服务
        /// </summary>
        /// <param name="services">服务集合</param>
        /// <returns>Rpc服务构建者</returns>
        public static IRpcBuilder AddRpcCore(this IServiceCollection services)
        {
            if (services == null)
                throw new ArgumentNullException(nameof(services));

            //services.AddSingleton<ICacheProvider, DefaultRedisCacheProvider>();
            services.AddSingleton<IServiceIdGenerator, DefaultServiceIdGenerator>();
            services.AddSingleton<ITypeConvertibleProvider, DefaultTypeConvertibleProvider>();
            services.AddSingleton<ITypeConvertibleService, DefaultTypeConvertibleService>();
            services.AddSingleton<IServiceRouteFactory, DefaultServiceRouteFactory>();

            return new RpcBuilder(services)
                .AddJsonSerialization()
                .UseJsonCodec();
        }

        public static IRpcBuilder AddClientProxy(this IRpcBuilder builder)
        {
            var services = builder.Services;

            services.AddSingleton<IServiceProxyGenerater, ServiceProxyGenerater>();
            services.AddSingleton<IServiceProxyFactory, ServiceProxyFactory>();
            var provider = services.BuildServiceProvider();  
            services.ClientProxyAll(provider);
            services.AddRoutes(provider);
            return builder;
        }

        public static void AddRoutes(this IServiceCollection services, ServiceProvider provider)
        {


            var serviceEntryManager = provider.GetService<IServiceEntryManager>();
            var IpAddressModel = provider.GetService<IpAddressModel>();
            var addressDescriptors = serviceEntryManager.GetEntries().Select(i => new ServiceRoute
            {
                ServiceDescriptor = i.Descriptor,

            });
            var serviceRouteManager = provider.GetService<IServiceRouteManager>();
            serviceRouteManager.SetRoutesAsync(addressDescriptors, IpAddressModel).Wait();
        }

        /// <summary>
        /// 初始化所有Server端代理注入
        /// </summary>
        public static void ClientProxyAll(this IServiceCollection services, ServiceProvider provider)
        {

            IServiceProxyGenerater serviceProxyGenerate = provider.GetRequiredService<IServiceProxyGenerater>();
            IServiceProxyFactory serviceProxyFactory = provider.GetRequiredService<IServiceProxyFactory>();


            var path = AppDomain.CurrentDomain.BaseDirectory;
            var files = new DirectoryInfo(path).GetFiles();
            var types = (from file in files
                         where file.Extension.ToLower().Contains(".dll")
                         where !file.FullName.Contains("MySql.Data") ///EF5.0 BUG
                         from type in Assembly.LoadFrom(file.ToString()).GetExportedTypes()
                         where type.IsInterface && type.GetCustomAttributes(typeof(ServiceBundleAttribute), true).Length > 0
                         select type).ToList();

            var objs = serviceProxyGenerate.GenerateProxys(types, new List<string> { "ESTAR.Infrastructure" })
                .Select(proxy => serviceProxyFactory.CreateProxy(proxy, provider))
                .ToList();

            foreach (var item in objs)
            {
                var imItem = item.GetType();
                var iService = types.Where(n => imItem.GetInterfaces().Contains(n)).FirstOrDefault();
                if (iService != null)
                {
                    var imp = provider.GetService(iService);
                    if (imp == null)
                    {
                        services.AddSingleton(iService, imItem);
                        provider = services.BuildServiceProvider();
                    }
                }
            }

        }


        public static IRpcBuilder AddClient(this IServiceCollection services)
        {
            return services
                .AddRpcCore()
                .AddClientRuntime()
                .AddClientProxy();
        }

        /// <summary>
        /// 使用DotNetty进行传输。
        /// </summary>
        /// <param name="builder">Rpc服务构建者。</param>
        /// <returns>Rpc服务构建者。</returns>
        public static IRpcBuilder UseDotNettyTransport(this IRpcBuilder builder)
        {
            var services = builder.Services;

            services.AddSingleton<ITransportClientFactory, DefaultDotNettyTransportClientFactory>();

            services.AddSingleton<DefaultDotNettyServerMessageListener>();

            services.AddSingleton<IServiceHost, DefaultServiceHost>(
                provider => new DefaultServiceHost(
                    async endPoint =>
                    {
                        var messageListener = provider.GetRequiredService<DefaultDotNettyServerMessageListener>();
                        await messageListener.StartAsync(endPoint);
                        return messageListener;
                    },
                    provider.GetRequiredService<IServiceExecutor>()
                )
            );

            return builder;
        }

        /// <summary>
        /// 添加RPC服务认证 | 认证在服务端
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="authorizationServerProvider"></param>
        /// <returns></returns>
        public static IRpcBuilder AddAuthentication(this IRpcBuilder builder, Type authorizationServerProvider = null)
        {
            var services = builder.Services;

            services.AddSingleton(typeof(IAuthorizationServerProvider),
                authorizationServerProvider == null ? typeof(DefaultAuthorizationServerProvider) : authorizationServerProvider);

            return builder;
        }

        private static List<Assembly> _referenceAssembly = new List<Assembly>();
        private static List<Assembly> GetReferenceAssembly(params string[] virtualPaths)
        {
            var refAssemblies = new List<Assembly>();//Assembly 通过此类能够载入操纵一个程序集，并获取程序集内部信息
            var rootPath = AppContext.BaseDirectory;
            var existsPath = virtualPaths.Any();//判断是否有数据
            //if (existsPath && !string.IsNullOrEmpty(CPlatformAppConfig.ServerOptions.RootPath))
            //    rootPath = CPlatformAppConfig.ServerOptions.RootPath;
            var result = _referenceAssembly;
            if (!result.Any() || existsPath)
            {
                var paths = virtualPaths.Select(m => Path.Combine(rootPath, m)).ToList();
                if (!existsPath) paths.Add(rootPath);
                paths.ForEach(path =>
                {
                    var assemblyFiles = GetAllAssemblyFiles(path);

                    foreach (var referencedAssemblyFile in assemblyFiles)
                    {
                        var referencedAssembly = Assembly.LoadFrom(referencedAssemblyFile);
                        if (!_referenceAssembly.Contains(referencedAssembly))
                            _referenceAssembly.Add(referencedAssembly);
                        refAssemblies.Add(referencedAssembly);
                    }
                    result = existsPath ? refAssemblies : _referenceAssembly;
                });
            }
            return result;
        }
        private static List<string> GetAllAssemblyFiles(string parentDir)
        {
            var notRelatedFile = "";
            var relatedFile = "";
            var pattern = string.Format("^Microsoft.\\w*|^System.\\w*|^Netty.\\w*|^Autofac.\\w*{0}",
               string.IsNullOrEmpty(notRelatedFile) ? "" : $"|{notRelatedFile}");
            Regex notRelatedRegex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
            Regex relatedRegex = new Regex(relatedFile, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
            if (!string.IsNullOrEmpty(relatedFile))
            {
                return
                    Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFullPath).Where(
                        a => !notRelatedRegex.IsMatch(a) && relatedRegex.IsMatch(a)).ToList();
            }
            else
            {
                return
                    Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFullPath).Where(
                        a => !notRelatedRegex.IsMatch(a)).ToList();
            }
        }


        private static string[] GetPaths(params string[] virtualPaths)
        {
            var directories = new List<string>(virtualPaths.Where(p => !string.IsNullOrEmpty(p)));
            string rootPath =
                AppContext.BaseDirectory;
            var virPaths = virtualPaths;
            foreach (var virtualPath in virtualPaths)
            {

                var path = Path.Combine(rootPath, virtualPath);
                //if (_logger.IsEnabled(LogLevel.Debug))
                //    _logger.LogDebug($"准备查找路径{path}下的目录。");
                if (Directory.Exists(path))
                {
                    var dirs = Directory.GetDirectories(path);
                    directories.AddRange(dirs.Select(dir => Path.Combine(virtualPath, new DirectoryInfo(dir).Name)));
                }
                else
                {
                    //if (_logger.IsEnabled(LogLevel.Debug))
                    //    _logger.LogDebug($"未找到路径：{path}。");
                    directories.Remove(virtualPath);
                    virPaths = null;
                }
            }
            return directories.Any() ? directories.Distinct().ToArray() : virPaths;
        }
        public static List<Type> GetInterfaceService(this IServiceCollection builder, params string[] virtualPaths)
        {
            var types = new List<Type>();
            var referenceAssemblies = GetReferenceAssembly(virtualPaths);
            referenceAssemblies.ForEach(p =>
            {
                types.AddRange(p.GetTypes()
                    .Where(t => t.GetCustomAttributes(typeof(ServiceBundleAttribute), true).Length > 0
                           && t.IsInterface));
            });
            return types;


        }
        private static List<Type> GetDataContract(params string[] virtualPaths)
        {
            var types = new List<Type>();
            var referenceAssemblies = GetReferenceAssembly(virtualPaths);
            referenceAssemblies.ForEach(p =>
            {
                types.AddRange(p.GetTypes()
                    .Where(t => t.GetCustomAttributes(typeof(DataContractAttribute), true).Length > 0));
            });
            return types;


        }
        public static IEnumerable<string> GetDataContractName(this IServiceCollection builder, params string[] virtualPaths)
        {
            var namespaces = new List<string>();
            var assemblies = builder.GetInterfaceService(virtualPaths)
                .Union(GetDataContract(virtualPaths))
                .Select(p => p.Assembly)
                .Distinct()
                .ToList();

            assemblies.ForEach(assembly =>
            {
                var cc = assembly.GetTypes();
                namespaces.AddRange(assembly.GetTypes().Where(t => t.GetCustomAttribute<DataContractAttribute>() != null).Select(n => n.Namespace));
            });
            return namespaces.Distinct();
        }
    }
}